![Boto3の補完を可能にするboto3-stubsを使ってみる](https://devio2023-media.developers.io/wp-content/uploads/2019/04/python.png)
Boto3の補完を可能にするboto3-stubsを使ってみる
Boto3とインテリセンス
Boto3はAWSを使う上でとても便利なPythonのパッケージです。
ただ、個人的には一つだけ難点があってそれは補完が効かないことです。 AWSには数多のサービスがあってその各メソッドを覚えておくなんてことは自分には無理でした。
その都度、公式のBoto3のドキュメントを参照し、引数や返り値を確認するのが常となってました。
あー、Boto3でも補完が効いたらな。
でも、こんな引数で呼び足す形でクライアントのインスタンス作成してたら補完なんてできるわけないよな。
import boto3 s3 = boto3.client('s3') # <- 引数で返ってくるインスタンスの種類が変わるので、実行までクラスを判別できないと思っていた
そんな思いに応えてくれるパッケージがありました。
近しいパッケージでboto-stubsというのがありますが、それとは別です。
boto3-stubs自体はBoto3のタイプアノテーション用に型情報を提供してくれるパッケージなのですが、その副次作用として補完が使えるようになります。
当然、mypyを使用した型チェックも可能です。(すばらしい!)
インストール
pipやCondaを利用してインストールすることが可能です。 このパッケージをインストール後、エディターが対応していれば補完を行うことができます。 自分はVSCodeを使っているのですが、無事補完が効くようになりました。
インストールは以下のように使用する、AWSサービスの単位でインストールします。 インストールする際はBoto3と同じバージョンのものを使うようにしましょう。
pip insall boto3-stubs['s3']
ちなみにVSCodeの場合は「AWS Boto3」という拡張を使ってこのパッケージをインストールすることも可能です。
拡張機能をインストール後にVS Codeの拡張で「AWS Boto3: Auto-Discover Boto3 services in current project」というコマンドを実行すると、現在のプロジェクト内で使用しているAWSのサービス用の型情報のパッケージをリストアップしてくれます。
チェックがついているものがインストールされます。
補完がどのくらい効くのか確認する
client
Boto3ではboto3.client
を使用してAWSサービスのクライアントを作成可能です。
これにはローレベルのAPIがメソッドとして備わっていて、その補完が効くのか試してみましょう。
以下はサービスのクライアントを作成するときのサンプルコードです。
import boto3 s3 = boto3.client('s3') # <- ここではS3のクライアントを作成している。
s3.
と打ってみて補完が出てくるかを見てみます。
公式リファレンスにあるメソッドが列挙されています。 引数までしっかりと教えてくれています。 少し意地悪してDynamoDBのクライアントも作成しましたが、混同することなく補完が効いています。
resource
Boto3では先程のclient
以外にも、resource
APIというものがあります。
こちらは、より高レベルなAPIとなっていて、よく使うようなアクセスパターンをメソッドとして実装し、より手軽に使えるようになっています。
以下ではS3について取り上げますが、代表的なリソースとしては「Bucket,Object」など馴染みのある単語のリソースがあります。
import boto3 s3 = boto3.resource('s3') bucket = s3.Bucket('xxx') objs = bucket.objects.all()
どんなリソースがあるのか補完を確認してみます。
次にリソースにどんなメソッドが実装されているのか補完を確認します。
これについても公式リファレンスと同様のものが表示されています。
paginator
Boto3ではリスト形式で複数のアイテムが返ってくるようなAPIに関してはページネータを利用できる場合があります。
ページネータについてはこちらの記事の後半が参考になるかと思います。
以下のように使用したいメソッド名を引数に渡して、ページネータを作成します。
ページネータのpaginate
メソッドに、使用したい関数と同じ引数を渡すことで、イテレータが返ってきます。
import boto3 s3 = boto3.client('s3') paginator = s3.get_paginator('list_objects_v2') pages = paginator.paginate(Bucket='xxx') # <- ここはlist_objects_v2の引数と同じ
ページネータについて補完が効くか見てみます。
画像中のオレンジの枠で囲った部分に注目してほしいのですが、しっかりとlist_objects_v2
と同じものになってます。
しかもちゃんと、ページネーションに使用するPaginationConfig
まで引数にあります!
ここまででも十分すごいのですが、更に便利なことがあります。
返ってきた結果についても補完が効きました。 Dict型で返ってくるのですが、どんなキーがあるのか補完してくれます。
型チェック
最初の紹介でも述べたように、Boto3のタイプアノテーションとして利用できるのでmypyを利用した型チェックも可能です。
以下のようなコードで実行しています。
bucket.objcets.all()
で得られる結果はObjectSummaryであり、Objectとは同一で無いはずです。
なので、このコードで型チェックを行うと、エラーが発生するのが期待されます。
import boto3 s3 = boto3.resource('s3') bucket = s3.Bucket('xxxxx') objs = bucket.objects.all() from mypy_boto3_s3.service_resource import Object def get_obj_key(obj: Object): return obj.key obj_keys = [get_obj_key(o) for o in objs]
実際に型チェックしてみます。
$ mypy type_check.py type_check.py:12: error: Argument 1 to "get_obj_key" has incompatible type "ObjectSummary"; expected "Object" [arg-type] Found 1 error in 1 file (checked 1 source file)
しっかりとエラーになりました。
Union型を使用してみる
Object
とObjectSummary
は両方ともkeyプロパティを持っているので、get_obje_key
はどちらでも使えるようにしたいです。
Union型を使用してみます。
以下のコードならば型チェックは通るはずです。
import boto3 from typing import Union s3 = boto3.resource('s3') bucket = s3.Bucket('xxxxx') objs = bucket.objects.all() from mypy_boto3_s3.service_resource import Object, ObjectSummary def get_obj_key(obj: Union[Object, ObjectSummary]): # <- Union型に変更 return obj.key obj_keys = [get_obj_key(o) for o in objs]
実際に型チェックしてみます。
$ mypy type_check.py Success: no issues found in 1 source file
無事成功しました。
Union型に不要な型を入れてみる
最後に少し意地悪して、key
プロパティを持たないBucket
型をUnionに混ぜてみます。
これは当然型チェックは通らないはずです。
import boto3 from typing import Union s3 = boto3.resource('s3') bucket = s3.Bucket('xxxxx') objs = bucket.objects.all() from mypy_boto3_s3.service_resource import Object, ObjectSummary, Bucket def get_obj_key(obj: Union[Object, ObjectSummary, Bucket]): # <- Bucket型を追加 return obj.key obj_keys = [get_obj_key(o) for o in objs]
実際に型チェックしてみます。
$ mypy type_check.py type_check.py:11: error: Item "Bucket" of "Union[Object, ObjectSummary, Bucket]" has no attribute "key" [union-attr] Found 1 error in 1 file (checked 1 source file)
期待通り失敗してくれました。
最後に
Boto3の場合client
メソッドなどの仕様から、動的に型が決まるので、補完や型チェックは難しいだろうなと思い込んでいました。
ですが、boto3-stubsはそれを可能にしてくれました。
これでBoto3を使ったコードの開発もより効率化されるかと思います。